package de.medieninf.android.sensimg;

import java.util.concurrent.ConcurrentHashMap;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.ToggleButton;

public class SensImg extends Activity {
	final static String TAG = "sens_img";
	final static String ACCEL_VIEW_TEMPLATE = "x:%4.2f  y:%4.2f  z:%4.2f";
	final static long MIN_PAUSE_SCREEN_UPDATE=50; // msecs

	private ToggleButton mToggleSensor;
//	private ToggleButton mToggleFake;
	private ToggleButton mToggleSend;
	private ToggleButton mToggleBall;
	private Button mButtonImageView;
	private Button mButtonKeypadView;
	private TextView mSensorValueX;
	private TextView mSensorValueY;
	private TextView mSensorValueZ;
	private BallView mBallView;
	
	private SensorService mSensorService;	
	private UDPSender mUDPSender;
	private KeyboardState mKeyboardState;
	
	private OnClickListener onToggle = new OnClickListener() {
		@Override
		public void onClick(View view) {
			if (mToggleSensor.isChecked()) {
				mSensorService.register(accelerationListener);
				mSensorService.startMonitoring();
			} else {
				mSensorService.stopMonitoring();
				mSensorService.unregister(accelerationListener);
			}
		}
	};
		
	private OnClickListener onSend = new OnClickListener() {
		@Override
		public void onClick(View view) {
			if (mToggleSend.isChecked()) {
				if (mUDPSender != null) {
					mUDPSender.close();
				}
				mUDPSender = new UDPSender(mKeyboardState);
				mUDPSender.setHost(mSensorService.getCurrentHostSend());
				mUDPSender.setPort(mSensorService.getCurrentPortSend());
				mSensorService.register(mUDPSender);
			} else {
				if (mUDPSender != null) {
					mSensorService.unregister(mUDPSender);
					mUDPSender.close();
				}
				mUDPSender = null;
			}
		}
	};
	
	private OnClickListener onBall = new OnClickListener() {
		@Override
		public void onClick(View view) {
			if (mToggleBall.isChecked()) {
				if (mBallView != null) {
					mSensorService.register(mBallView);
					mBallView.setKeyboardState(mKeyboardState);
				}
			} else {
				if (mBallView != null) {
					mSensorService.unregister(mBallView);
					mBallView.setKeyboardState(null);
				}
			}
		}
	};
	
	private OnClickListener onShowGraphics = new OnClickListener() {
		@Override
		public void onClick(View v) {
			Intent intent = new Intent();
			intent.setClass(SensImg.this, ImageViewActivity.class);
			startActivity(intent);
		}
	};

	private OnClickListener onShowKeypad = new OnClickListener() {
		@Override
		public void onClick(View v) {
			Intent intent = new Intent();
			intent.setClass(SensImg.this, KeypadViewActivity.class);
			startActivity(intent);
		}
	};

	// Simple cache for display values, reduce GC activity 
	private ConcurrentHashMap<Float, String> floatToStringCache = new ConcurrentHashMap<Float, String>();
	private String cachedFloatToString(float v) {
		Float f = v;
		if (!floatToStringCache.containsKey(f)) {
			floatToStringCache.put(f, floatToString(v));
			// Log.v(TAG, "" + floatToStringCache.entrySet().size());
		}		
		return floatToStringCache.get(f);
	}
		
	private String floatToString(float v) {
		// cannot use format because it is way too slow...
		String sign = (v < 0) ? "-" : " ";
		v = (v < 0) ? -v : v;
		int iv = (int) v;
		String before = Integer.toString(iv);
		int i=2-before.length();
		switch (i) {
		case 0:
			break;
		case 1:
			before = "0" + before;
			break;
		}
		int av = (int) (((v-iv)*1000));
		if (av >= 1000)
			av = 999;
		String after = Integer.toString(av);
		i = 3-after.length();
		switch (i) {
		case 0:
			break;
		case 1:
			after = "0" + after;
			break;
		case 2:
			after = "00" + after;
			break;
		}
		String ret = sign + before + "." + after;
		// Log.v(TAG, "floatToString: " + ret);
		return ret;
	}
	
	// UI update of the acceleration values emitted
	private AccelerationListener accelerationListener = new AccelerationListener() {
		private volatile float lx, ly, lz;
		private volatile long lastUpdate = 0;
		Runnable uiUpdate = new Runnable() {
			public void run() {
		        mSensorValueX.setText(cachedFloatToString(lx));			
				mSensorValueY.setText(cachedFloatToString(ly));
				mSensorValueZ.setText(cachedFloatToString(lz));
			}
		};
		@Override
		public void update(float x, float y, float z) {
			long now = System.currentTimeMillis();
			if (now-lastUpdate < MIN_PAUSE_SCREEN_UPDATE) { // do not update UI too often...
				return;
			}
			lastUpdate = now;
			lx = x;
			ly = y;
			lz = z;
			// hopefully not much overhead, but works with fake as well
			runOnUiThread(uiUpdate);
		}
	};
	
    private ServiceConnection mConnection = new ServiceConnection() {
    	@Override
        public void onServiceConnected(ComponentName className, IBinder service) {
    		Log.v(TAG, "trying to bind SensorService");
            mSensorService = ((SensorService.LocalBinder) service).getService();
            mKeyboardState = mSensorService.getKeyboardState();
            // update the state according to the buttons; for restart after Configuration change
            onToggle.onClick(mToggleSensor);
            onSend.onClick(mToggleSend);
            onBall.onClick(mToggleBall);
        }
		@Override
		public void onServiceDisconnected(ComponentName name) {
    		Log.v(TAG, "SensorService is disconnected");
			mSensorService = null;
		}
    };
	
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mSensorValueX = (TextView) findViewById(R.id.accel_valuex);
        mSensorValueY = (TextView) findViewById(R.id.accel_valuey);
        mSensorValueZ = (TextView) findViewById(R.id.accel_valuez);
        mToggleSensor = (ToggleButton) findViewById(R.id.toggle_sensor);
        mToggleSensor.setOnClickListener(onToggle);        
        mToggleSend = (ToggleButton) findViewById(R.id.toggle_send);
        mToggleSend.setOnClickListener(onSend);
        //mToggleFake = (ToggleButton) findViewById(R.id.toggle_fake);
        //mToggleFake.setOnClickListener(onFake);
        mToggleBall = (ToggleButton) findViewById(R.id.toggle_ball);
        mToggleBall.setOnClickListener(onBall);
        mButtonImageView = (Button) findViewById(R.id.show_graphics);
        mButtonImageView.setOnClickListener(onShowGraphics);
        mButtonKeypadView = (Button) findViewById(R.id.show_keypad);
        mButtonKeypadView.setOnClickListener(onShowKeypad);
        mBallView = (BallView) findViewById(R.id.ball_canvas);
        accelerationListener.update(0.0f, 0.0f, 0.0f);
        mBallView.invalidate();
        bindService(
        		new Intent(SensImg.this, SensorService.class),
                mConnection, 
                Context.BIND_AUTO_CREATE);
    }
	
	@Override
	protected void onResume() {
		super.onResume();
		if (mSensorService != null) {
			String host = mSensorService.getCurrentHostSend();
			int port = mSensorService.getCurrentPortSend();
			if (mUDPSender != null) {
				if (!mUDPSender.getHost().equals(host) ||
						(mUDPSender.getPort() != port)) {
					mUDPSender.setHost(host);
					mUDPSender.setPort(port);					
				}
			}
			// Log.v(TAG, "settings sending to " + host + ":" + port);
		}		
	}
	
	@Override
	protected void onPause() {
		// don't do anything - keep the connection
		super.onPause();
	}
	
	@Override
	protected void onDestroy() {
    	if (mSensorService != null) {
    		unbindService(mConnection);
    		mSensorService = null;
    	}
		super.onDestroy();
	}
	
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    	super.onCreateOptionsMenu(menu);
    	MenuInflater mi = new MenuInflater(getApplication());
    	mi.inflate(R.menu.imgsens_menu, menu);
    	return true;
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
    	switch (item.getItemId()) {
    	case R.id.opt_settings:
    		Intent intent = new Intent();
    		intent.setClass(this, SettingsActivity.class);
    		startActivity(intent);
    		// Log.v(TAG, "Settings selected");
    		return true;
    	case R.id.opt_quit:
    		finish();
    		return true;
    	}
    	return true;
    }
	
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
    	super.onConfigurationChanged(newConfig);
    	// don't do anything as necessary stuff is handled by BallView
    	// just implement it in order to make sure that the activity is
    	// not restarted
    }
}